\chapter{系统详细实现}
\section{智能合约}
考虑到查询的复杂性与高效性，对于Solidity自带的Mapping
实现可能导致数据冗余存储，故将前面所述的核心数据存储为数组，
通过Solidity自带的Mapping完成对数据在数组中位置的映射，
实现少量空间冗余换取查询上的时间节省。
另外为了对于实现细节的封装，
将DID公开上链部分分割为DID Method与DID公开上链的其他部分，
实现添加DID时检查DID Method是否有效。
具体为：
在内部使用\_Did数据结构存储实现细节，
提供开放数据结构Did供用户使用，同样DID Method也是如此。
另外需要对DID文档的增加与修改进行严格的访问控制，
在本文中主要通过solidity的modifier实现。
另外对于DID的实现有些许差异，
即实现去中心化中对等实体理念，在算法程序层面将
数字身份认证提供商与普通人平等对待，
将信任与否的权利移交给终端应用程序，
为此，我们将自签名证书与其他证书统一，
将每个实体都初始化一个默认身份，
即为did:eth:ethereum\_address，
对于相比于传统的根证书，本文的根证书不再具有特殊性，
即为以默认身份签发的证书，任何人都可发行。
另外对于该默认身份是否引入新的问题讨论如下：
因为本文是基于以太坊的，
故任何与智能合约的交互都会有一个以太坊地址，
故该默认身份不会引入任何问题。

\subsection{添加DID方法}

\begin{figure}[h]
  \centerline{\includegraphics[scale=1]{code_addDidMethod.png}}
  \caption{添加DID Method函数}
  \label{fig:addDidMethod}
\end{figure}

在添加DID Method时需要进行DID Method的重复性检查，防止重复性添加相同的DID Method。

\subsection{添加DID具体标识符}

\begin{figure}[h]
  \centerline{\includegraphics[scale=0.8]{code_addDid.png}}
  \caption{添加DID函数}
  \label{code:addDid}
\end{figure}

在添加DID时需要进行如下检查DID中的属性，如代码\ref{code:DidStruct}所示
\begin{enumerate}
  \item 检查didMethodName 是否已经存在，
        即不允许在未添加Did Method的情况下添加该Did Method下的Did。
  \item 检查当前添加的身份是否已经添加，即不允许重复添加相同的DID身份。
  \item 检查使用的身份是否属于调用者即身份认证提供商。
\end{enumerate}

通过solidity的modifier检查是否符合条件具体如下图\ref{code:addDid}：
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.3]{truffle_addDid.jpg}}
  \caption{添加DID函数的测试结果}
  \label{fig:truffle_addDid}
\end{figure}
在truffle中测试结构如图\ref{fig:truffle_addDid}

\subsection{撤销DID与更新DID}
在使DID无效时只需要修改DID中的属性，
如代码\ref{code:updateDid}所示，
其中将每个属性的更新分开。对于撤销DID，
需要检查当前操作用户是否为该DID对应管理员，
只有该DID的创建者才有权利撤销DID，使DID无效。
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.9]{code_updateDid.png}}
  \caption{更新DID函数}
  \label{code:updateDid}
\end{figure}

\section{身份认证提供商}
\label{sec:身份认证提供商}
在身份认证提供商部分，需要完成如下两部分工作：
\begin{itemize}
  \item 将DID文档的可公开部分存储到以太坊区块链。
  \item 签发证书供用户下载。
\end{itemize}
另外在实现身份认证提供商时，考虑到各身份认证提供商接口实现的差异性，
即任何第三方在接入时都需要查阅身份认证提供商的接入文档。
类似于在做QQ登录、微信登录的接入时，虽然有OAuth的约束，
但两家的实现也有不同，做QQ登录需要查阅QQ提供的接入文档，
而在做微信登录时还需要查阅微信提供的接入文档，两者代码并不通用。
所以各家身份认证提供商的证书格式并未完全标准化，
只是如\ref{sec:Merkle proof}章节中所叙述的那样，
提供必要的信息，而对应信息字段名并未标准化。

\subsection{前端界面}
前端界面主要完成了用户信息申请认证的认证页面和
认证后的用户信息的下载页面。

\begin{figure}[h]
  \centerline{\includegraphics[scale=0.75]{ca_submit.jpg}}
  \caption{身份认证提供商认证页面}
  \label{fig:ca_submit}
\end{figure}

在用户访问数字身份认证商时，用户需要登录
Metamask以方便前端程序获取用户当前的以太坊地址，
如图\ref{fig:ca_submit}所示，
前端程序会将从Metamask获取的以太坊地址
与用户填写的信息通过axios提交到后端。
用户可以查询到该信息的审核状态。
在审核完成后，用户可以下载认证之后的证书。

\subsection{区块链存储}
在用户提交完认证信息且管理员认证后，
后端将会通过node-orm2访问sqlite3数据库，
提取出隐私的部分，并通过web3js的
contract.methods.addDid().send(\{from: ethAddress,gas:1000000\})
调用智能合约代码，将用户可公开部分的信息提交到区块链存储。

\subsection{证书签发}
在区块链完成存储后，再从数据库读取该用户的DID文档非公开部分
对这些数据构建Merkle 树，获取Merkle 树的树根的Hash值Merkle Root,
再对Merkle Root进行签名，签名主要用到了web3js.eth.accounts.sign
（merkle\_root，private\_key）
在签名完成后将签名结果存入数据库，然后将用户的签发状态更改为已签发。
具体代码如下：
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.75]{code_addDid_js.png}}
  \caption{调用智能合约的代码}
  \label{code:addDid_js}
\end{figure}
然后用户即可下载证书，
证书下载后为json格式的文本文件，
内容如下图所示：
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.75]{ca_json.png}}
  \caption{身份认证商签发证书的结构与内容}
  \label{fig:ca_json}
\end{figure}
其中address为用户的以太坊地址，salt\_seed即为
\ref{sec:Merkle proof}小节中叙述的解决安全问题的Salt Seed，
merkle\_index为构建Merkle树时的节点次序，
merkle\_root\_signature
即为认证商对Merkle Root值的签名。


\section{第三方网络服务提供商}
在第三方网络服务接入对应身份认证提供商时，
都需要像\ref{sec:身份认证提供商}章节叙述的那样，
按照身份认证提供商的接口，解析对应字段。
具体为在后端实现了用户选择性披露后的Merkle树重建与验证，
并通过web3js查询区块链上的签名链，
在本文中并未对签名链内容进行验证，只通过前端显示该签名链。
具体的代码如图\ref{code:Merkle Verify}：
\subsection{Merkle树重建并验证Merkle Root}
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.8]{code_merkle_verify.png}}
  \caption{重建Merkle树并验证的函数}
  \label{code:Merkle Verify}
\end{figure}

先根据Salt Seed与披露信息在Merkle树中的位置重建
会用到的Salt序列，再根据Salt序列、披露信息以及Proof数组重建整个Merkle树
并验证重建后的Merkle Root是否与原来的值相同。
在不同时即为用户修改了披露的信息，则验证失败，
然后验证签名解析出签名作者，
验证签名作者是否是链上存在的实体，如果不存在该实体，
即为用户自己伪造的链下实体进行签名，验证失败。
签名作者是链上存在的实体但该实体并不是系统可信实体时，
即为用户自己伪造了链上实体进行签名，验证失败。


\subsection{证书签发链的显示}
在显示签发链时主要通过递归查询实现，即为：
查询证书签发时数字身份认证提供商所使用的身份，
为避免混淆，将该待查询的身份称为子身份，
将新查询到的身份称为父身份。将父身份添加到签发链中，
继续查询该父身份签发时父身份的数字身份认证提供商所使用的身份，
将新查询到的身份称为爷爷身份，不断递归进行，
即可查询完成该数字身份所使用的整个签发链。
伪代码如下：
\begin{Cplus}
  global array = [];
  function findAll(id){
    array.push(id);
    father_id = 该id的父身份;
    if(father_id == null){
      return;
    }
    findAll(father_id);
  }
  call findAll(id);
\end{Cplus}

签发链如图\ref{fig:third_party_app}所示：
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.75]{third_party_app.jpg}}
  \caption{第三方验证后显示的签名链}
  \label{fig:third_party_app}
\end{figure}
\section{用户端选择性披露}
用户选择需要选择性披露的证书，对于证书中的身份信息重建Merkle树，
其中重建时的子节点顺序需要与身份认证提供商建立的Merkle树的顺序相同。
然后对于需要披露的部分按照\ref{sec:Merkle proof}小节中的叙述进行处理，
然后将披露后的信息存储为用户所指定的路径。
具体处理与处理过程如图\ref{fig:local_helper}
\begin{figure}[h]
  \centerline{\includegraphics[scale=0.5]{local.jpg}}
  \caption{用户端选择性披露处理}
  \label{fig:local_helper}
\end{figure}

\section{本章小结}
本章完成了\ref{charp:systemDesign}章节中的
所有设计要求，实现了基础的数字身份管理系统。
完成了证书中Merkle树的重建与认证，
并通过智能合约的接口验证了签名并显示了签名链。
